import WebGl from '@/modules/three';

const { Three, TWEEN, dat, Stats, OrbitControls, VertexNormalsHelper } = WebGl;

class WebGlControl {
  private container: any;
  private camera: any;
  private scene: any;
  private renderer: any;
  private controls: any;
  private stats: any;
  private animateFrame: any;
  private clock: any;

  private Geometrys: {
    planeGeometry?: any;
    triangleGeometry?: any;
  } = {}; //保存所有Geometry，方便统一销毁
  private Materials: {
    planeMaterial?: any;
    triangleMaterial?: any;
  } = {}; //保存所有Material，方便统一销毁

  private GuiInit: any = null;
  private guiData: any = {
    x: 0.005,
    color: '#fafafa',
  };
  private directionAnimation: {
    direction: any; // 方向
    directionTrend: any; // 方向变换的方向
    targetQuaternion: any; //4元数，表示旋转
    // speed: number  //速度
    // speedDecay: number  //速度衰减，-1,1
  } = {
    direction: new Three.Vector3(1, 0, 0),
    directionTrend: new Three.Vector3(0, 1, 0),
    targetQuaternion: new Three.Quaternion(),
    // speed: 15,
    // speedDecay: 0,
  };
  private rotationMatrix = new Three.Matrix4();
  private dirGroup: any;

  public init = (id: string) => {
    // if(this.scene) {return}
    this.container = document.getElementById(id);
    let width = this.container.clientWidth,
      height = this.container.clientHeight;

    this.clock = new Three.Clock();
    this.scene = new Three.Scene();

    this.camera = new Three.PerspectiveCamera(60, width / height, 1, 2000);
    this.camera.position.x = 0;
    this.camera.position.y = 20;
    this.camera.position.z = 500;
    this.camera.lookAt(this.scene.position);

    this.renderer = new Three.WebGLRenderer();
    // this.renderer.setClearColor( 0xffffff );
    this.renderer.setPixelRatio(window.devicePixelRatio);
    this.renderer.setSize(width, height);
    this.container.appendChild(this.renderer.domElement);

    this.controls = new OrbitControls(this.camera, this.renderer.domElement);

    this.addThings();
    this.addLight(); //+光源
    this.animate(); //开始动
    this.initStats();

    this.initGui();
  };
  public destroy = () => {
    this.toggleGui(0);
    Object.values(this.Geometrys).forEach((e) => {
      e.dispose();
    });
    Object.values(this.Materials).forEach((e) => {
      e.dispose();
    });
    this.scene.clear();
    this.renderer.dispose();
    this.renderer.forceContextLoss();
    this.camera = null;
    this.scene = null;
    this.renderer = null;
    this.GuiInit = null;
    this.stats = null;
    cancelAnimationFrame(this.animateFrame);
  };

  private initStats = () => {
    this.stats = new Stats();
    this.stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
    this.stats.dom.style.position = 'absolute';
    this.container.appendChild(this.stats.dom);
  };

  public initGui = () => {
    if (this.GuiInit) {
      this.toggleGui();
      return;
    }
    this.GuiInit = new dat.GUI({
      // autoPlace: false
    });

    this.GuiInit.domElement.style.position = 'absolute';
    this.GuiInit.domElement.style.right = '10px';
    this.GuiInit.domElement.style.top = '10px';

    let GuiController = this.GuiInit.addFolder('控制窗口');
    GuiController.add(this.guiData, 'x', -0.1, 0.1, 0.005).name('自转速度');
    GuiController.addColor(this.guiData, 'color').name('背景颜色');
    GuiController.open();

    // let cubeGui = this.GuiInit.addFolder('cube position');
    // cubeGui.add(this.cubeGroup.position, 'x', -100, 100, 1);
    // cubeGui.add(this.cubeGroup.position, 'y', -100, 100, 1);
    // cubeGui.add(this.cubeGroup.position, 'z', -100, 100, 1);
    // cubeGui.open();
  };

  public toggleGui = (state: any = null) => {
    if (!this.GuiInit || !this.GuiInit.domElement) {
      return;
    }
    let display = this.GuiInit.domElement.style.display == 'none' ? '' : 'none';
    if (state == 0) {
      display = 'none';
    }
    this.GuiInit.domElement.style.display = display;
  };

  private addLight = () => {
    let ambi = new Three.AmbientLight(0x686868);
    this.scene.add(ambi);

    let spotLight = new Three.DirectionalLight(0xffffff);
    spotLight.position.set(550, 100, 550);
    spotLight.intensity = 0.6;
    this.scene.add(spotLight);

    let light = new Three.PointLight(0xffff00);
    light.position.set(-100, 100, 0);
    this.scene.add(light);
  };

  private addThings = () => {
    const axesHelper = new Three.AxesHelper(800);
    this.scene.add(axesHelper); //添加坐标轴；红线是X轴，绿线是Y轴，蓝线是Z轴

    /** 平面 **/
    this.Geometrys.planeGeometry = new Three.PlaneGeometry(400, 400, 1, 1);
    // this.Materials.planeMaterial = new Three.MeshBasicMaterial({color: 0xffff00, side: Three.DoubleSide})
    this.Materials.planeMaterial = new Three.MeshBasicMaterial({
      color: 0x000000,
      wireframe: true,
    });
    //wireframe:  显示为网格
    const plane = new Three.Mesh(
      this.Geometrys.planeGeometry,
      this.Materials.planeMaterial,
    );
    plane.rotation.x = 90;
    this.scene.add(plane);
    /** 平面 **/

    this.addTriangles();
    this.addDirectionGeo();

    this.initTween();
  };
  private addTriangles = () => {
    const triangles = 5;
    const positions = new Float32Array(triangles * 3 * 3); //每个三角有三个顶点
    const normals = new Float32Array(triangles * 3 * 3); //每个顶点的法线
    const colors = new Float32Array(triangles * 3 * 3); //每个顶点的颜色，渐变
    const color = new Three.Color();
    this.Geometrys.triangleGeometry = new Three.BufferGeometry();
    const baseNum = 300,
      hfNum = 150;
    const trgSize = 100,
      hfSiz = 50;
    const pA = new Three.Vector3();
    const pB = new Three.Vector3();
    const pC = new Three.Vector3();

    const cb = new Three.Vector3();
    const ab = new Three.Vector3();
    for (let i = 0; i < positions.length; i += 9) {
      const x = Math.random() * baseNum - hfNum;
      const y = Math.random() * baseNum - hfNum;
      const z = Math.random() * baseNum - hfNum; //计算三角所处基本位置；
      /** 再分别计算三个顶点位置 **/
      const ax = (positions[i] = x + Math.random() * trgSize - hfSiz);
      const ay = (positions[i + 1] = y + Math.random() * trgSize - hfSiz);
      const az = (positions[i + 2] = z + Math.random() * trgSize - hfSiz);

      const bx = (positions[i + 3] = x + Math.random() * trgSize - hfSiz);
      const by = (positions[i + 4] = y + Math.random() * trgSize - hfSiz);
      const bz = (positions[i + 5] = z + Math.random() * trgSize - hfSiz);

      const cx = (positions[i + 6] = x + Math.random() * trgSize - hfSiz);
      const cy = (positions[i + 7] = y + Math.random() * trgSize - hfSiz);
      const cz = (positions[i + 8] = z + Math.random() * trgSize - hfSiz);
      /** 再分别计算三个顶点位置 **/

      pA.set(ax, ay, az);
      pB.set(bx, by, bz);
      pC.set(cx, cy, cz);

      cb.subVectors(pC, pB); //subVectors: 将该向量设置为a - b。
      ab.subVectors(pA, pB);
      cb.cross(ab); //cross: 该向量设置为它本身与传入的v的叉积

      cb.normalize(); //将该向量转换为单位向量(方向相同，长度设为1)

      const nx = cb.x;
      const ny = cb.y;
      const nz = cb.z;

      normals[i] = nx;
      normals[i + 1] = ny;
      normals[i + 2] = nz;

      normals[i + 3] = nx;
      normals[i + 4] = ny;
      normals[i + 5] = nz;

      normals[i + 6] = nx;
      normals[i + 7] = ny;
      normals[i + 8] = nz;

      // colors
      const vx = x / baseNum + 0.9;
      const vy = y / baseNum + 0.9;
      const vz = z / baseNum + 0.9;

      color.setRGB(vx, vy, vz);

      colors[i] = color.r;
      colors[i + 1] = color.g;
      colors[i + 2] = color.b;

      colors[i + 3] = color.r;
      colors[i + 4] = color.g;
      colors[i + 5] = color.b;

      colors[i + 6] = color.r;
      colors[i + 7] = color.g;
      colors[i + 8] = color.b;
    }
    this.Geometrys.triangleGeometry.setAttribute(
      'position',
      new Three.BufferAttribute(positions, 3),
    );
    this.Geometrys.triangleGeometry.setAttribute(
      'normal',
      new Three.BufferAttribute(normals, 3),
    );
    this.Geometrys.triangleGeometry.setAttribute(
      'color',
      new Three.BufferAttribute(colors, 3),
    );

    this.Geometrys.triangleGeometry.computeBoundingSphere();
    this.Materials.triangleMaterial = new Three.MeshPhongMaterial({
      color: 0xaaaaaa,
      specular: 0xffffff,
      shininess: 250,
      side: Three.DoubleSide,
      vertexColors: true,
    });
    const triangleMesh = new Three.Mesh(
      this.Geometrys.triangleGeometry,
      this.Materials.triangleMaterial,
    );
    this.scene.add(triangleMesh);

    const helper = new VertexNormalsHelper(triangleMesh, 20, 0xff0000, 2); //显示顶点向量长20，粗2
    this.scene.add(helper);
  };

  private addDirectionGeo = () => {
    this.dirGroup = new Three.Group();

    const geo = new Three.BoxGeometry(10, 100, 10);
    const mtl = new Three.MeshPhongMaterial({
      color: 0xfd59d7,
    });
    const cube = new Three.Mesh(geo, mtl);
    cube.castShadow = true;
    cube.receiveShadow = true;
    this.dirGroup.add(cube);

    const geometry = new Three.ConeGeometry(6, 16, 32);
    const material = new Three.MeshBasicMaterial({ color: 0xfd59d7 });
    const cone = new Three.Mesh(geometry, material);
    cone.position.set(0, 58, 0);
    this.dirGroup.add(cone);

    // console.log('matrix1:',this.rotationMatrix)
    // this.rotationMatrix.makeTranslation(0.1,0,0);
    // this.rotationMatrix.makeRotationX(0.01);

    // const translation = new Three.Vector3(0.1, 0, 0);
    // const rotation = new Three.Euler(0, 0, 0);
    // const scale = new Three.Vector3(1,1,1);
    this.rotationMatrix.compose(
      { x: 0.1, y: 0, z: 0 },
      { _x: 0, _y: 0, _z: 0, _w: 0 },
      { x: 1, y: 1.002, z: 1 },
    );
    console.log('matrix2:', this.rotationMatrix);
    // this.dirGroup.position.set(0.0001,0,0)

    this.scene.add(this.dirGroup);
  };

  private initTween = () => {
    // var tween = new TWEEN.Tween(this.cubeGroup.position)
    //   .to({ x: 200 }, 1000)
    //   .start();
  };

  private animate = () => {
    // if(!this.renderer){return;}
    if (this.stats) {
      this.stats.begin();
    }

    this.render();
    TWEEN.update();
    this.controls.update();

    if (this.stats) {
      this.stats.end();
    }
    this.animateFrame = requestAnimationFrame(this.animate);
  };

  private render = () => {
    const delta = this.clock.getDelta();
    // this.rotationMatrix.lookAt(this.directionAnimation.direction, new Three.Vector3(0,0,0), new Three.Vector3(0,0,0))
    // this.directionAnimation.targetQuaternion.setFromRotationMatrix( this.rotationMatrix );
    // 更新物体的方向
    // const speed = 10;
    // const step = speed * delta;
    // this.dirGroup.quaternion.rotateTowards( this.directionAnimation.targetQuaternion, step );
    // this.dirGroup.setRotationFromMatrix(this.rotationMatrix)
    // this.dirGroup.applyMatrix4(this.rotationMatrix);
    // this.dirGroup.updateMatrix()

    // console.log('this.rotationMatrix:', this.rotationMatrix)
    this.dirGroup.applyMatrix4(this.rotationMatrix);

    this.renderer.setClearColor(this.guiData.color, 1.0);
    // const delta = this.clock.getDelta();
    // console.log('delta', delta);
    this.renderer.render(this.scene, this.camera);
  };
}

const webGlControl = new WebGlControl();
export default webGlControl;
